
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>SpringBoot 解决项目统一功能 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>SpringBoot 解决项目统一功能 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">在企业级Java项目中，为何需要统一处理日志记录、异常处理、权限校验等功能？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">如果每个模块都单独实现这些通用功能，会导致代码冗余，增加维护成本。统一处理可以将这些通用功能与业务逻辑代码解耦，使代码更清晰、易于维护。</div>
          </div>
          <div class="card-source">来源: 1. 为什么需要统一功能？</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">文档中提到了哪三种Spring Boot提供的非侵入式方案来解决统一功能问题？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">文档中提到的三种主要方案是：拦截器（Interceptor）、AOP（切面编程）和全局异常处理（Exception Handler）。</div>
          </div>
          <div class="card-source">来源: 2. 解决统一功能的 SpringBoot 方案</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">根据文档，Spring AOP（切面编程）主要适用于哪类场景？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">AOP适用于方法级别的拦截或功能增强，例如事务管理、性能监控和权限校验等场景。</div>
          </div>
          <div class="card-source">来源: 2. 解决统一功能的 SpringBoot 方案</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">Spring Boot中的全局异常处理机制（使用@ControllerAdvice）有什么核心优势？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">其核心优势在于能够统一管理所有异常，提供统一的返回格式，并避免在业务代码中充斥大量繁琐的 try-catch 语句，从而提高代码的可读性和可维护性。</div>
          </div>
          <div class="card-source">来源: 5. 全局异常处理</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">在“使用AOP实现统一权限管理”的案例中，是如何将权限校验逻辑应用到特定API方法上的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">通过三个步骤实现：1. 创建一个自定义注解（如@AdminOnly）；2. 创建一个AOP切面，使用@Before("@annotation(AdminOnly)")来拦截带有该注解的方法；3. 在需要进行权限校验的API方法上添加@AdminOnly注解。</div>
          </div>
          <div class="card-source">来源: 4. 实战案例 2：使用 AOP 实现统一权限管理</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">在“使用拦截器实现统一日志管理”的案例中，HandlerInterceptor接口的哪两个方法被用来记录请求开始和结束的信息？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">preHandle方法用于在请求开始前记录请求路径和参数，并记下开始时间；afterCompletion方法用于在请求结束后计算并记录请求耗时。</div>
          </div>
          <div class="card-source">来源: 3. 实战案例 1：使用拦截器实现统一日志管理</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">根据文档总结，拦截器（Interceptor）和AOP在应用范围上有什么主要区别？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">主要区别在于拦截器适用于全局请求拦截，如日志记录和请求统计；而AOP更适用于方法级别的功能增强，如权限校验和事务管理。</div>
          </div>
          <div class="card-source">来源: 6. 总结</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">拦截器（Interceptor）适用于哪些具体的统一功能场景？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">拦截器适用于全局请求拦截的场景，例如日志记录、权限校验、请求统计和统一参数处理。</div>
          </div>
          <div class="card-source">来源: 2. 解决统一功能的 SpringBoot 方案 & 6. 总结</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">在实现日志拦截器时，文档中的代码是如何精确计算每个请求的耗时的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">通过使用ThreadLocal<Long>变量。在preHandle方法中存入当前时间戳（System.currentTimeMillis()），然后在afterCompletion方法中取出该时间戳，与当前时间相减，从而得到请求的耗时，并且ThreadLocal保证了多线程环境下的线程安全。</div>
          </div>
          <div class="card-source">来源: 3. 实战案例 1：使用拦截器实现统一日志管理</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
